/* -*-c++-*- OpenThreads library, Copyright (C) 2008  The Open Thread Group
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version.  The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* OpenSceneGraph Public License for more details.
*/

#ifndef _ATOMIC_
#define _ATOMIC_

//#include <OpenThreads/Config>
//#include <OpenThreads/Exports>
#include "kernel/OgrePlatform.h"
#include "kernel/dll_cfg.h"

#ifdef __OS_WIN__
    #define _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED
#else
    #define _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS
#endif

#if defined(_OPENTHREADS_ATOMIC_USE_BSD_ATOMIC)
# include <libkern/OSAtomic.h>
# define _OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES
#elif defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) && defined(__i386__)
# define _OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES
#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED)
# define _OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
# include <atomic.h>
# include "Mutex.h"
# include "ScopedLock"
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
# include "Mutex.h"
# include "ScopedLock"
#endif

#if defined(_OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES)
#define _OPENTHREADS_ATOMIC_INLINE
#else
#define _OPENTHREADS_ATOMIC_INLINE inline
#endif

namespace FD
{
	namespace Kernel
	{
		/**
		*  @class CAtomic
		*  @brief  This class provides an atomic increment and decrement operation.
		*/
		class FD_Kernel_Export CAtomic {
		public:
			CAtomic(unsigned value = 0) : _value(value)
			{ }
			_OPENTHREADS_ATOMIC_INLINE unsigned operator++();
			_OPENTHREADS_ATOMIC_INLINE unsigned operator--();
			_OPENTHREADS_ATOMIC_INLINE unsigned AND(unsigned value);
			_OPENTHREADS_ATOMIC_INLINE unsigned OR(unsigned value);
			_OPENTHREADS_ATOMIC_INLINE unsigned XOR(unsigned value);
			_OPENTHREADS_ATOMIC_INLINE unsigned exchange(unsigned value = 0);
			_OPENTHREADS_ATOMIC_INLINE operator unsigned() const;

			_OPENTHREADS_ATOMIC_INLINE bool ref()
			{
				++(*this);
				return this->_value != 0;
			}
			_OPENTHREADS_ATOMIC_INLINE bool deref()
			{
				--(*this);
				return this->_value != 0;
			}
		private:

			CAtomic(const CAtomic&);
			CAtomic& operator=(const CAtomic&);

#if defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			mutable Mutex _mutex;
#endif
#if defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED)
			volatile long _value;
#elif defined(_OPENTHREADS_ATOMIC_USE_BSD_ATOMIC)
			volatile int32_t _value;
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			volatile uint_t _value;
			mutable Mutex _mutex;  // needed for xor
#else
			volatile unsigned _value;
#endif
		};

		/**
		*  @class CAtomicPtr
		*  @brief  This class provides an atomic pointer assignment using cas operations.
		*/
		class FD_Kernel_Export CAtomicPtr {
		public:
			CAtomicPtr(void* ptr = 0) : _ptr(ptr)
			{ }
			~CAtomicPtr()
			{ _ptr = 0; }

			// assigns a new pointer
			_OPENTHREADS_ATOMIC_INLINE bool assign(void* ptrNew, const void* const ptrOld);
			_OPENTHREADS_ATOMIC_INLINE void* get() const;

		private:
			CAtomicPtr(const CAtomicPtr&);
			CAtomicPtr& operator=(const CAtomicPtr&);

#if defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			mutable Mutex _mutex;
#endif
			void* volatile _ptr;
		};

#if !defined(_OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES)

		_OPENTHREADS_ATOMIC_INLINE unsigned
			CAtomic::operator++()
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_add_and_fetch(&_value, 1);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __add_and_fetch(&_value, 1);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			return atomic_inc_uint_nv(&_value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			return ++_value;
#else
			return ++_value;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE unsigned
			CAtomic::operator--()
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_sub_and_fetch(&_value, 1);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __sub_and_fetch(&_value, 1);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			return atomic_dec_uint_nv(&_value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			return --_value;
#else
			return --_value;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE unsigned
			CAtomic::AND(unsigned value)
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_fetch_and_and(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __and_and_fetch(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			return atomic_and_uint_nv(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			_value &= value;
			return _value;
#else
			_value &= value;
			return _value;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE unsigned
			CAtomic::OR(unsigned value)
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_fetch_and_or(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __or_and_fetch(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			return atomic_or_uint_nv(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			_value |= value;
			return _value;
#else
			_value |= value;
			return _value;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE unsigned
			CAtomic::XOR(unsigned value)
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_fetch_and_xor(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __xor_and_fetch(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			ScopedLock<Mutex> lock(_mutex);
			_value ^= value;
			return _value;
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			_value ^= value;
			return _value;
#else
			_value ^= value;
			return _value;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE unsigned
			CAtomic::exchange(unsigned value)
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_lock_test_and_set(&_value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __compare_and_swap(&_value, _value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			return atomic_cas_uint(&_value, _value, value);
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			unsigned oldval = _value;
			_value = value;
			return oldval;
#else
			unsigned oldval = _value;
			_value = value;
			return oldval;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE
			CAtomic::operator unsigned() const
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			__sync_synchronize();
			return _value;
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			__synchronize();
			return _value;
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			membar_consumer(); // Hmm, do we need???
			return _value;
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			return _value;
#else
			return _value;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE bool
			CAtomicPtr::assign(void* ptrNew, const void* const ptrOld)
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			return __sync_bool_compare_and_swap(&_ptr, (void *)ptrOld, ptrNew);
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			return __compare_and_swap((unsigned long*)&_ptr, (unsigned long)ptrOld, (unsigned long)ptrNew);
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			return ptrOld == atomic_cas_ptr(&_ptr, const_cast<void*>(ptrOld), ptrNew);
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			if (_ptr != ptrOld)
				return false;
			_ptr = ptrNew;
			return true;
#else
			if (_ptr != ptrOld)
				return false;
			_ptr = ptrNew;
			return true;
#endif
		}

		_OPENTHREADS_ATOMIC_INLINE void*
			CAtomicPtr::get() const
		{
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS)
			__sync_synchronize();
			return _ptr;
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS)
			__synchronize();
			return _ptr;
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN)
			membar_consumer(); // Hmm, do we need???
			return _ptr;
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX)
			ScopedLock<Mutex> lock(_mutex);
			return _ptr;
#else
			return _ptr;
#endif
		}

#endif // !defined(_OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES)

		template <typename T>
		inline void qAtomicAssign(T *&d, T *x)
		{
			if (d == x)
				return;
			x->ref.ref();
			if (!d->ref.deref())
				delete d;
			d = x;
		}

		template <typename T>
		inline void qAtomicDetach(T *&d)
		{
			if (d->ref == 1)
				return;
			T *x = d;
			d = new T(*d);
			if (!x->ref.deref())
				delete x;
		}
	}
}

#endif // _OPENTHREADS_ATOMIC_
